
#include <scene/sky.h>
#include <gfx/mesh.h>
#include <gfx/vertex.h>
#include <gfx/iindexbuffer.h>
#include <gfx/ivertexbuffer.h>
#include <gfx/ivertexdeclaration.h>
#include <gfx/renderenvironment.h>
#include <scene/geometry.h>
#include <engine.h>



ceAbstractSky::SkyAnimator::SkyAnimator ()
{
  CE_OBJECT_CONSTR
}


void ceAbstractSky::SkyAnimator::Animate(ceNode *node, ceRenderEnvironment &env)
{
  node->SetTranslation(env.CameraLocationGlobal);
  node->FinishTransformation(false);
}


ceAbstractSky::ceAbstractSky ()
{
  CE_OBJECT_CONSTR

}

ceAbstractSky::~ceAbstractSky ()
{
  CE_UNSET(_animator);
}


bool ceAbstractSky::Initialize(iDevice *device)
{
  ceMesh* mesh = CreateMesh(device);
  if (!mesh)
    {
      return false;
    }

  iStaticMesh* staticMesh = device->CreateStaticMesh();
  if (!staticMesh)
    {
      mesh->Release();
      return false;
    }
	staticMesh->SetMesh (mesh);

  ceGeometry* geometry = new ceGeometry (staticMesh);
  SetGeometry(geometry);
  staticMesh->Release();
  geometry->Release();

  _animator = new ceAbstractSky::SkyAnimator ();
  SetAnimator(_animator);
  _animator->Release();

  // skys get never clipped
  SetClip(false);

  return true;
}



ceSkyBox::ceSkyBox()
{
}


bool ceSkyBox::Initialize(iDevice *device, const ceVector3f &dimensions)
{
  _dimension = dimensions;
  return ceAbstractSky::Initialize(device);
}

ceRef<ceMesh> ceSkyBox::CreateMesh(iDevice* device)
{
  struct Vertex
  {
    ceVector3f xyz;
    ceVector3f txt;
  };

  const ceVector3f d = _dimension;

  Vertex vertices [] = {
    { ceVector3f (-d.x, -d.y, -d.z), ceVector3f (-1, -1, -1) },
    { ceVector3f (-d.x, -d.y,  d.z), ceVector3f (-1, -1,  1) },
    { ceVector3f (-d.x,  d.y, -d.z), ceVector3f (-1,  1, -1) },
    { ceVector3f (-d.x,  d.y,  d.z), ceVector3f (-1,  1,  1) },
    { ceVector3f ( d.x, -d.y, -d.z), ceVector3f ( 1, -1, -1) },
    { ceVector3f ( d.x, -d.y,  d.z), ceVector3f ( 1, -1,  1) },
    { ceVector3f ( d.x,  d.y, -d.z), ceVector3f ( 1,  1, -1) },
    { ceVector3f ( d.x,  d.y,  d.z), ceVector3f ( 1,  1,  1) }
  };

  unsigned short indices [] ={
    0, 2, 6,    0, 6, 4,          // bottom
    2, 3, 7,    2, 7, 6,          // front
    6, 7, 5,    6, 5, 4,          // right
    4, 5, 1,    4, 1, 0,          // back
    0, 1, 3,    0, 3, 2,          // left
    1, 5, 7,    1, 7, 3,          // top
  };

	ceVertexElement elements [] = {
		ceVertexElement (ceProgramParameterName (PPB_Position0), DT_Float, 3,  0, 24, 0),
		ceVertexElement (ceProgramParameterName (PPB_TexCoord0), DT_Float, 3, 12, 24, 0),
		ceVertexElement (),
	};

	iVertexBuffer* vb = device->CreateVertexBuffer (sizeof (vertices), vertices, BDM_Static);
	iIndexBuffer* ib = device->CreateIndexBuffer (sizeof (indices), indices, BDM_Static);
	iVertexDeclaration* vd = device->CreateVertexDeclaration (elements);

	ceBoundingBox bbox;
	bbox.Clear ();
	bbox.Add (_dimension);
	bbox.Add (-_dimension);
	bbox.Update ();

	ceMesh* mesh = new ceMesh ();
	mesh->SetVertexDeclaration (vd);
	mesh->SetVertices (vb);
	mesh->SetIndices (ib);
	mesh->Set32BitIndices (false);
	mesh->SetPrimitiveType (PT_Triangles);
	mesh->SetNumberOfTrigons (12);
	mesh->SetBoundingBox (bbox);

  CE_UNSET(vb);
  CE_UNSET(ib);
  CE_UNSET(vd);


	return mesh;
}


ceSkySphere::ceSkySphere()
{
}


bool ceSkySphere::Initialize (iDevice *device, float radius, unsigned details)
{
  _radius = radius;
  _details = details;
  return ceAbstractSky::Initialize(device);
}

ceRef<ceMesh> ceSkySphere::CreateMesh(iDevice* device)
{
  struct Vertex
  {
    ceVector3f xyz;
    ceVector3f txt;
  };

  ceVertexElement elements []  =
    { ceVertexElement(ceProgramParameterName(PPB_Position0), DT_Float, 3, 0,  24, 0),
      ceVertexElement(ceProgramParameterName(PPB_TexCoord0), DT_Float, 3, 12, 24, 0),
      ceVertexElement()
    };

  unsigned numH = _details;
  unsigned numV = _details / 2;

  unsigned numVertices = numH * (numV+1);
  Vertex* vertices = new Vertex [numVertices];
  Vertex* vptr = vertices;

  for (unsigned v=0; v<numV; v++)
    {
      float angleV = (float)M_PI * (float)v / (float)(numV-1);

      for (unsigned h=0; h<numH; h++)
        {
          float angleH = (float)M_PI * 2.0f * (float)h / (float)numH;

          ceVector3f po;
          po.x = sin(angleH) * _radius * sin(angleV);
          po.y = cos(angleH) * _radius * sin(angleV);
          po.z = cos(angleV) * _radius * 1.0f;

          vptr->xyz = po;
          vptr->txt = po.Normalized();
          vptr++;
        }
    }
  fflush(stdout);

  unsigned numTrigons = (numV-1) * numH * 2;

  unsigned numIndices = numTrigons * 3;
  unsigned short *indices = new unsigned short [numIndices];
  unsigned short *iptr = indices;
  for (unsigned v=0; v<(numV-1); v++)
    {
      unsigned i0 = v     * numH;
      unsigned i1 = (v+1) * numH;

      for (unsigned h=0; h<numH; h++)
        {
          unsigned h0 = h;
          unsigned h1 = h+1;
          if (h1 == numH)
            {
              h1 = 0;
            }

          unsigned i00 = i0 + h0;
          unsigned i10 = i1 + h0;
          unsigned i01 = i0 + h1;
          unsigned i11 = i1 + h1;

          *iptr++ = (unsigned short)(i00 & 0x0000ffff);
          *iptr++ = (unsigned short)(i01 & 0x0000ffff);
          *iptr++ = (unsigned short)(i11 & 0x0000ffff);

          *iptr++ = (unsigned short)(i00 & 0x0000ffff);
          *iptr++ = (unsigned short)(i11 & 0x0000ffff);
          *iptr++ = (unsigned short)(i10 & 0x0000ffff);
        }
    }

  iVertexBuffer* vb = device->CreateVertexBuffer (sizeof (Vertex) * numVertices, vertices, BDM_Static);
  iIndexBuffer* ib = device->CreateIndexBuffer (sizeof (unsigned short) * numIndices, indices, BDM_Static);
  iVertexDeclaration* vd = device->CreateVertexDeclaration (elements);

  ceBoundingBox bbox;
  bbox.Clear ();
  bbox.Add (ceVector3f (_radius, _radius, _radius));
  bbox.Add (ceVector3f (-_radius, -_radius, -_radius));
  bbox.Update ();

  ceMesh* mesh = new ceMesh ();
  mesh->SetVertexDeclaration (vd);
  mesh->SetVertices (vb);
  mesh->SetIndices (ib);
  mesh->Set32BitIndices (false);
  mesh->SetPrimitiveType (PT_Triangles);
  mesh->SetNumberOfTrigons (numTrigons);
  mesh->SetBoundingBox (bbox);

  CE_UNSET(vb);
  CE_UNSET(ib);
  CE_UNSET(vd);

  delete [] indices;
  delete [] vertices;

  return mesh;
}

